package com.xuanwei.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.xuanwei.core.entity.bo.NameBo;
import com.xuanwei.core.entity.vo.*;
import com.xuanwei.core.entity.constant.CouponConstant;
import com.xuanwei.core.entity.param.Coupon;
import com.xuanwei.core.enums.CouponLimitType;
import com.xuanwei.core.enums.CouponType;
import com.xuanwei.core.enums.NavigatePosition;
import com.xuanwei.core.exception.XwException;
import com.xuanwei.core.service.*;
import com.xuanwei.dao.entity.*;
import com.xuanwei.dao.mapper.XwCouponMapper;
import com.xuanwei.xwcommon.util.BeanUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券 服务实现类
 * </p>
 *
 * @author mjy
 * @since 2019-07-16
 */
@Service
public class XwCouponServiceImpl extends ServiceImpl<XwCouponMapper, XwCoupon> implements IXwCouponService {

    private IXwUserCouponService userCouponService;

    private IXwProductService productService;

    private IXwHomepageNavigateService homepageNavigateService;

    private IXwNavigateProductService navigateProductService;

    public XwCouponServiceImpl(IXwUserCouponService userCouponService,
                               IXwHomepageNavigateService homepageNavigateService,
                               IXwProductService productService,
                               IXwNavigateProductService navigateProductService) {
        this.userCouponService = userCouponService;
        this.productService = productService;
        this.homepageNavigateService = homepageNavigateService;
        this.navigateProductService = navigateProductService;
    }


    @Override
    @Transactional
    public void obtainCoupon(Integer userId, Integer id) {
        XwCoupon coupon = getOne(new QueryWrapper<XwCoupon>()
                .eq("id", id)
                .eq("`status`", 0));
        if (coupon == null) {
            throw new XwException("该优惠券不存在");
        }
        if (coupon.getExpireType() == 0 && coupon.getExpireEndTime().isBefore(LocalDate.now())) {
            throw new XwException("该优惠券过期");
        }
        XwUserCoupon userCoupon = userCouponService.getOne(new QueryWrapper<XwUserCoupon>()
                .select("id")
                .eq("coupon_id", id)
                .eq("user_id", userId)
                .ge("coupon_expire_time", LocalDateTime.now())
                .eq("`status`", 0));
        if (userCoupon != null) {
            throw new XwException("您已领取过该优惠券");
        }
        //悲观锁
        int total = userCouponService.count(new QueryWrapper<XwUserCoupon>()
                .eq("coupon_id", id)
                .last("for update"));
        if (total >= coupon.getLimitSendTotal()) {
            throw new XwException("该优惠券已抢光");
        }
        int count = userCouponService.count(new QueryWrapper<XwUserCoupon>()
                .eq("coupon_id", id)
                .eq("user_id", userId));

        if (count >= coupon.getLimitPre()) {
            throw new XwException("每人限制领取" + coupon.getLimitPre() + "张");
        }
        XwUserCoupon xwUserCoupon = new XwUserCoupon();
        xwUserCoupon.setUserId(userId);
        xwUserCoupon.setCouponAmount(coupon.getAmount());
        xwUserCoupon.setCouponId(coupon.getId());
        xwUserCoupon.setCouponType(coupon.getType());
        xwUserCoupon.setCouponConditionAmount(coupon.getConditionAmount());
        xwUserCoupon.setCouponExplain(coupon.getExplainText());
        xwUserCoupon.setCouponLimitType(coupon.getLimitType());
        xwUserCoupon.setCouponObjId(coupon.getObjId());
        xwUserCoupon.setCouponTitle(coupon.getTitle());
        xwUserCoupon.setObtainTime(LocalDateTime.now());
        if (coupon.getExpireType() == 0) {
            xwUserCoupon.setCouponExpireTime(LocalDateTime.of(coupon.getExpireEndTime(), LocalTime.of(23, 59, 59)));
        }
        if (coupon.getExpireType() == 1) {
            xwUserCoupon.setCouponExpireTime(LocalDateTime.now().plusDays(coupon.getExpireDays()));
        }
        userCouponService.save(xwUserCoupon);
    }

    @Override
    public void add(Coupon coupon) {
        XwCoupon xwCoupon = new XwCoupon();
        BeanUtils.copyProperties(coupon, xwCoupon);
        xwCoupon.setCreateTime(LocalDateTime.now());
        if (coupon.getLimitType().equals(CouponLimitType.LIMIT_ACTIVITY.getCode())) {
            XwHomepageNavigate one = homepageNavigateService.getOne(new QueryWrapper<XwHomepageNavigate>()
                    .select("id")
                    .eq("id", coupon.getObjId())
                    .eq("position", NavigatePosition.ACTIVITY.getCode()));
            if (one == null) {
                throw new XwException("该活动不存在");
            }
        }

        if (coupon.getLimitType().equals(CouponLimitType.LIMIT_PRODUCT.getCode())) {
            XwProduct one = productService.getOne(new QueryWrapper<XwProduct>()
                    .select("id")
                    .eq("id", coupon.getObjId())
                    .eq("`status`", 0));
            if (one == null) {
                throw new XwException("该商品不存在");
            }
        }
        save(xwCoupon);
    }

    @Override
    public void update(Integer id, String explainText, Integer limitSendTotal, Integer limitPre) {
        XwCoupon one = getOne(new QueryWrapper<XwCoupon>()
                .select("id")
                .eq("id", id)
                .eq("status", 0));

        if (one == null) {
            throw new XwException("该优惠券不存在");
        }
        one.setExplainText(explainText);
        one.setLimitSendTotal(limitSendTotal);
        one.setLimitPre(limitPre);
        updateById(one);
    }

    @Override
    public void updateStatus(Integer id) {
        XwCoupon coupon = baseMapper.selectOne(new QueryWrapper<XwCoupon>()
                .select("id")
                .eq("id", id)
                .eq("`status`", 0));
        if (coupon == null) {
            throw new XwException("该不存在该优惠卷");
        }
        coupon.setStatus(1);
        updateById(coupon);
    }


    @Override
    public Page<CouponListVo> list(Integer type, Integer limitType, String name, Page<XwCoupon> page) {
        QueryWrapper<XwCoupon> wrapper = new QueryWrapper<>();
        if (type != null) {
            wrapper.eq("type", type);
        }
        if (limitType != null) {
            wrapper.eq("limit_type", limitType);
        }
        if (StringUtils.isNotBlank(name)) {
            wrapper.like("title", name);
        }
        wrapper.orderByDesc("create_time");
        page(page, wrapper);
        Page<CouponListVo> pageData = new Page<>(page.getCurrent(), page.getSize());
        List<CouponListVo> couponListVos = Lists.newArrayListWithCapacity(page.getRecords().size());
        page.getRecords().forEach(s -> {
            CouponListVo couponListVo = new CouponListVo();
            couponListVo.setId(s.getId());
            couponListVo.setAmount(s.getAmount());
            couponListVo.setCreateTime(s.getCreateTime());
            couponListVo.setType(s.getType());
            couponListVo.setTitle(s.getTitle());
            couponListVo.setStatus(s.getStatus());
            if (s.getExpireType().equals(0) && s.getExpireEndTime().isBefore(LocalDate.now())) {
                couponListVo.setStatus(2);
            }
            if (s.getType().equals(CouponType.TO_AMOUNT.getCode())) {
                couponListVo.setLimitType(String.format(CouponConstant.LIMIT_AMOUNT, s.getConditionAmount().intValue()));
            } else if (s.getType().equals(CouponType.NO_CONDITION.getCode())) {
                couponListVo.setLimitType(CouponConstant.NO_LIMIT_AMOUNT);
            }

            if (s.getLimitType() == 0) {
                couponListVo.setCondition(CouponConstant.NO_LIMIT_PRODUCT);
            } else if (s.getLimitType() == 1) {
                couponListVo.setCondition(CouponConstant.LIMIT_ACTIVITY);
            } else if (s.getLimitType() == 2) {
                couponListVo.setCondition(CouponConstant.LIMIT_PRODUCT);
            }

            Integer limitSendTotal = s.getLimitSendTotal();
            int obtainNum = userCouponService.count(new QueryWrapper<XwUserCoupon>()
                    .eq("coupon_id", s.getId()));
            couponListVo.setObtainRate(obtainNum + "/" + limitSendTotal);
            int useNum = userCouponService.count(new QueryWrapper<XwUserCoupon>()
                    .eq("coupon_id", s.getId())
                    .eq("status", 1));
            couponListVo.setUseNum(useNum);
            couponListVos.add(couponListVo);
        });
        pageData.setRecords(couponListVos);
        pageData.setTotal(page.getTotal());
        pageData.setPages(page.getPages());
        return pageData;
    }

    @Override
    public void delete(Integer id) {
        XwCoupon coupon = getOne(new QueryWrapper<XwCoupon>().select("id,`status`,expire_end_time").eq("id", id));
        if (coupon == null) {
            throw new XwException("数据不存在");
        }
        if (coupon.getStatus() == 1 || coupon.getExpireEndTime().isBefore(LocalDate.now())) {
            baseMapper.deleteById(id);
        } else {
            throw new XwException("活动中的不能删除");
        }

    }

    @Override
    public List<ProductSimpleVo> search(Integer productId, String name) {

        QueryWrapper<XwProduct> wrapper = new QueryWrapper<>();

        if (productId != null) {
            wrapper.eq("id", productId);
        }

        if (StringUtils.isNotBlank(name)) {
            wrapper.like("`name`", name);
        }

        wrapper.eq("`status`", 0);

        wrapper.select("id,`name`,main_image,price,stock,status");

        wrapper.last("limit 10");

        List<XwProduct> list = productService.list(wrapper);

        return BeanUtil.copyList(list, ProductSimpleVo.class);
    }

    @Override
    public List<NameBo> findAllActivity() {
        List<XwHomepageNavigate> position = homepageNavigateService.list(new QueryWrapper<XwHomepageNavigate>()
                .select("id,`name`")
                .eq("position", NavigatePosition.ACTIVITY.getCode()));
        return BeanUtil.copyList(position, NameBo.class);
    }

    @Override
    public List<CouponProductVo> findProductCoupon(Integer userId) {
        QueryWrapper<XwCoupon> wrapper = new QueryWrapper<>();
        wrapper.select("id,type,condition_amount,amount,expire_type,expire_end_time,expire_start_time,obj_id");
        wrapper.eq("limit_type", 2)
                .eq("`status`", 0);

        List<XwCoupon> list = list(wrapper);
        List<CouponProductVo> couponProductVos = Lists.newArrayListWithCapacity(list.size());

        list.forEach(s -> {
            Integer objId = s.getObjId();
            XwProduct one = productService.getOne(new QueryWrapper<XwProduct>()
                    .select("id,`name`,min_price,main_image")
                    .eq("id", objId)
                    .eq("status", 0));
            if (one != null) {
                CouponProductVo couponProductVo = new CouponProductVo();
                couponProductVo.setId(s.getId());
                if (s.getType().equals(CouponType.TO_AMOUNT.getCode())) {
                    couponProductVo.setAmountCondition(String.format(CouponConstant.LIMIT_AMOUNT, s.getConditionAmount().intValue()));
                }
                if (s.getType().equals(CouponType.NO_CONDITION.getCode())) {
                    couponProductVo.setAmountCondition(CouponConstant.NO_LIMIT_AMOUNT);
                }
                couponProductVo.setProductId(one.getId());
                couponProductVo.setMainImage(one.getMainImage());
                couponProductVo.setName(one.getName());
                couponProductVo.setPrice(one.getMinPrice());
                couponProductVo.setAmount(s.getAmount());
                if (s.getExpireType() == 0) {
                    couponProductVo.setExpireTime(String.format(CouponConstant.LIMIT_TIME,
                            s.getExpireStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                            s.getExpireEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
                }
                if (s.getExpireType() == 1) {
                    couponProductVo.setExpireTime(String.format(CouponConstant.LIMIT_DAYS, s.getExpireDays()));
                }

                couponProductVo.setRealPrice(one.getMinPrice().subtract(s.getAmount()));

                if (userId != null) {
                    QueryWrapper<XwUserCoupon> queryWrapper = new QueryWrapper<XwUserCoupon>()
                            .select("id")
                            .eq("coupon_id", s.getId())
                            .eq("`status`", 0)
                            .eq("user_id", userId)
                            .ge("coupon_expire_time", LocalDateTime.now());

                    XwUserCoupon userCoupon = userCouponService.getOne(queryWrapper);
                    couponProductVo.setStatus(userCoupon != null ? 1 : 0);
                } else {
                    couponProductVo.setStatus(0);
                }

                couponProductVos.add(couponProductVo);
            }
        });
        return couponProductVos;
    }

    @Override
    public List<ActivityCouponVo> findExceptCoupon(Integer userId) {
        QueryWrapper<XwCoupon> wrapper = new QueryWrapper<>();
        wrapper.select("id,title,limit_type,`type`,condition_amount,amount,expire_days,expire_type,expire_end_time,expire_start_time,obj_id,explain_text");
        wrapper.ne("limit_type", 2)
                .eq("`status`", 0);
        List<XwCoupon> list = list(wrapper);
        List<ActivityCouponVo> couponVos = Lists.newArrayListWithCapacity(list.size());
        list.forEach(s -> {
            ActivityCouponVo activityCouponVo = new ActivityCouponVo();
            activityCouponVo.setId(s.getId());
            activityCouponVo.setExplain(s.getExplainText());
            activityCouponVo.setActivityId(s.getObjId());
            activityCouponVo.setAmount(s.getAmount());
            activityCouponVo.setTitle(s.getTitle());

            if (s.getType().equals(CouponType.TO_AMOUNT.getCode())) {
                activityCouponVo.setAmountCondition(String.format(CouponConstant.LIMIT_AMOUNT, s.getConditionAmount().intValue()));
            }
            if (s.getType().equals(CouponType.NO_CONDITION.getCode())) {
                activityCouponVo.setAmountCondition(CouponConstant.NO_LIMIT_AMOUNT);
            }


            if (userId != null) {
                QueryWrapper<XwUserCoupon> queryWrapper = new QueryWrapper<XwUserCoupon>()
                        .select("id")
                        .eq("coupon_id", s.getId())
                        .eq("`status`", 0)
                        .ge("coupon_expire_time", LocalDateTime.now());
                queryWrapper.eq("user_id", userId);
                XwUserCoupon userCoupon = userCouponService.getOne(queryWrapper);
                activityCouponVo.setStatus(userCoupon != null ? 1 : 0);
            } else {
                activityCouponVo.setStatus(0);
            }

            if (CouponLimitType.NO_LIMIT.getCode().equals(s.getLimitType())) {
                XwHomepageNavigate one = homepageNavigateService.getOne(new QueryWrapper<XwHomepageNavigate>()
                        .select("id")
                        .eq("position", NavigatePosition.SHOP_RECOMMEND.getCode()));
                List<ActivityCouponVo.Product> productByNavigate = findProductByNavigate(one.getId());
                activityCouponVo.setProducts(productByNavigate);

            }

            if (CouponLimitType.LIMIT_ACTIVITY.getCode().equals(s.getLimitType())) {
                List<ActivityCouponVo.Product> productByNavigate = findProductByNavigate(s.getObjId());
                activityCouponVo.setProducts(productByNavigate);
            }
            couponVos.add(activityCouponVo);
        });
        return couponVos;
    }

    private List<ActivityCouponVo.Product> findProductByNavigate(Integer navigateId) {
        List<XwNavigateProduct> navigateProducts = navigateProductService
                .list(new QueryWrapper<XwNavigateProduct>()
                        .eq("delete_flag", 0)
                        .eq("navigate_id", navigateId
                        ).last("limit 3"));
        List<XwProduct> products = productService.list(new QueryWrapper<XwProduct>()
                .select("id,`name`,main_image,min_price")
                .in("id", navigateProducts
                        .stream()
                        .map(XwNavigateProduct::getProductId)
                        .collect(Collectors.toList())));
        List<ActivityCouponVo.Product> productList = Lists.newArrayListWithCapacity(products.size());
        products.forEach(i -> {
            ActivityCouponVo.Product product = new ActivityCouponVo.Product();
            product.setId(i.getId());
            product.setMainImage(i.getMainImage());
            product.setPrice(i.getMinPrice());
            product.setName(i.getName());
            productList.add(product);
        });
        return productList;
    }

    @Override
    public List<CouponVo> findCouponByProduct(Integer productId, Integer userId) {
        QueryWrapper<XwCoupon> wrapper = new QueryWrapper<>();
        wrapper.select("id,amount,expire_type,expire_end_time,expire_start_time,obj_id");
        wrapper.eq("`status`", 0);
        List<XwCoupon> list = list(wrapper);
        List<CouponVo> couponVos = Lists.newArrayListWithCapacity(list.size());
        list.forEach(s -> {
            if (isUsable(productId, s)) {
                CouponVo couponVo = buildCouponVo(s, userId);
                couponVos.add(couponVo);
            }
        });
        return couponVos;
    }

    /**
     * 判断该优惠券是否可用,只判断商品不判断价格
     *
     * @param productId 商品ID
     * @param coupon    优惠券
     * @return true or false
     */
    private boolean isUsable(Integer productId, XwCoupon coupon) {
        //判断是否在活动中
        if (coupon.getLimitType().equals(CouponLimitType.LIMIT_ACTIVITY.getCode())) {
            XwNavigateProduct one = navigateProductService.getOne(new QueryWrapper<XwNavigateProduct>()
                    .eq("navigate_id", coupon.getObjId())
                    .eq("product_id", productId));
            return one != null;
        }

        if (coupon.getLimitType().equals(CouponLimitType.LIMIT_PRODUCT.getCode())) {
            return productId.equals(coupon.getObjId());
        }

        return true;
    }

    /**
     * 构建优惠券展示实体
     *
     * @param s 优惠券
     * @return 数据
     */
    private CouponVo buildCouponVo(XwCoupon s, Integer userId) {
        CouponVo couponVo = new CouponVo();
        couponVo.setId(s.getId());
        couponVo.setAmount(s.getAmount());
        couponVo.setTitle(s.getTitle());
        if (s.getType().equals(CouponType.NO_CONDITION.getCode())) {
            couponVo.setAmountCondition(CouponConstant.NO_LIMIT_AMOUNT);
        }

        if (s.getType().equals(CouponType.TO_AMOUNT.getCode())) {
            couponVo.setAmountCondition(String.format(CouponConstant.LIMIT_AMOUNT, s.getConditionAmount().intValue()));
        }
        couponVo.setProductCondition(s.getExplainText());
        if (s.getExpireType() == 0) {
            couponVo.setExpireTime(String.format(CouponConstant.LIMIT_TIME,
                    s.getExpireStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                    s.getExpireEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))));
        }
        if (s.getExpireType() == 1) {
            couponVo.setExpireTime(String.format(CouponConstant.LIMIT_DAYS, s.getExpireDays()));
        }

        if (userId != null) {
            QueryWrapper<XwUserCoupon> wrapper = new QueryWrapper<XwUserCoupon>()
                    .select("id")
                    .eq("coupon_id", s.getId())
                    .eq("`status`", 0)
                    .ge("coupon_expire_time", LocalDateTime.now());
            wrapper.eq("user_id", userId);
            XwUserCoupon userCoupon = userCouponService.getOne(wrapper);
            couponVo.setStatus(userCoupon != null ? 1 : 0);
        } else {
            couponVo.setStatus(0);
        }
        return couponVo;
    }
}
